package com.shiroha.common.utils;

import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * 文件工具类（异步操作，下载文件为同步）
 */
public class FileUtils {

    /**
     * 同步上传文件
     *
     * @param file      上传的文件
     * @param generateRandomName 是否生成随机文件名
     * @param uploadDir 存储文件的目录
     * @return 上传后文件相对于 {@code uploadDir} 的相对路径
     * @throws IOException 文件上传异常
     */
    public static String uploadFile(MultipartFile file, boolean generateRandomName, String uploadDir) throws IOException {
        Path dirPath = Paths.get(uploadDir);
        if (!Files.exists(dirPath)) {
            Files.createDirectories(dirPath);
        }

        String fileName = generateRandomName ? FileUtils.generateRandomFileName(file) : file.getOriginalFilename();
        if (fileName == null || fileName.isEmpty()) {
            throw new IOException("文件名无效");
        }

        Path destinationPath = dirPath.resolve(fileName);
        file.transferTo(destinationPath);
        return dirPath.relativize(destinationPath).toString();
    }

    /**
     * 上传文件，替换原始文件名
     *
     * @param file 上传的文件
     * @param uploadDir 存储文件的目录
     * @return 上传后文件相对于 {@code uploadDir}的相对路径
     * @throws IOException 上传异常
     */
    public static String uploadFile(MultipartFile file, String fileName, String uploadDir) throws IOException {
        Path dirPath = Paths.get(uploadDir);
        if (!Files.exists(dirPath)) {
            Files.createDirectories(dirPath);
        }

        if (fileName == null || fileName.isEmpty()) {
            throw new IOException("文件名无效");
        }

        Path destinationPath = dirPath.resolve(fileName);
        file.transferTo(destinationPath);
        return dirPath.relativize(destinationPath).toString();
    }

    /**
     * 异步上传文件
     *
     * @param file      上传的文件
     * @param uploadDir 存储文件的目录
     * @return 上传后文件相对于 {@code uploadDir} 的相对路径
     */
    public static CompletableFuture<String> uploadFileAsync(MultipartFile file, boolean generateRandomName,  String uploadDir) {
        return CompletableFuture.supplyAsync(() -> {
            try {
                return uploadFile(file, generateRandomName, uploadDir);
            } catch (IOException e) {
                throw new RuntimeException("文件上传失败", e);
            }
        });
    }

    /**
     * 异步上传文件，替换原始文件名
     *
     * @param file      上传的文件
     * @param fileName  自定义文件名
     * @param uploadDir 存储文件的目录
     * @return 上传后文件相对于 {@code uploadDir} 的相对路径
     */
    public static CompletableFuture<String> uploadFileAsync(MultipartFile file, String fileName, String uploadDir) {
        return CompletableFuture.supplyAsync(() -> {
            try {
                return uploadFile(file, fileName, uploadDir);
            } catch (IOException e) {
                throw new RuntimeException("文件上传失败", e);
            }
        });
    }

    /**
     * 同步下载文件
     *
     * @param uploadDir 上传目录
     * @param fileName  文件名
     * @return 文件
     * @throws IOException 文件不存在
     */
    public static File downloadFile(String uploadDir, String fileName) throws IOException {
        Path filePath = Paths.get(uploadDir, fileName);
        if (!Files.exists(filePath)) {
            throw new IOException("文件不存在");
        }
        return filePath.toFile();
    }

    /**
     * 将文件编码为 Base64 字符串
     *
     * @param imagePath 文件路径
     * @return Base64 编码的字符串
     * @throws IOException 文件读取异常
     */
    public static String encodeFileToBase64(String imagePath) throws IOException {
        byte[] fileBytes = Files.readAllBytes(Paths.get(imagePath));
        return Base64.getEncoder().encodeToString(fileBytes);
    }


    /**
     * 异步删除文件
     *
     * @param uploadDir 上传目录
     * @param fileName  文件名
     * @return 是否删除成功
     */
    public static CompletableFuture<Boolean> deleteFileAsync(String uploadDir, String fileName) {
        return CompletableFuture.supplyAsync(() -> {
            try {
                Path path = Paths.get(uploadDir, fileName);
                if (!Files.exists(path)) {
                    return false;
                }
                Files.delete(path);
                return true;
            } catch (IOException e) {
                throw new RuntimeException("文件删除失败", e);
            }
        });
    }

    /**
     * 异步列出目录下所有文件
     *
     * @param directory 目录
     * @return 文件列表
     */
    public static CompletableFuture<List<File>> listFilesInDirAsync(String directory) {
        return CompletableFuture.supplyAsync(() -> {
            try {
                Path dirPath = Paths.get(directory);
                if (!Files.exists(dirPath)) {
                    throw new IOException("目录不存在");
                }
                if (!Files.isDirectory(dirPath)) {
                    throw new IOException("路径不是目录");
                }

                try (Stream<Path> paths = Files.walk(dirPath, 1)) {
                    return paths
                            .skip(1) // 跳过目录本身
                            .map(Path::toFile)
                            .collect(Collectors.toList());
                }
            } catch (IOException e) {
                throw new RuntimeException("列出文件失败", e);
            }
        });
    }

    /**
     * 异步批量上传文件
     *
     * @param files     上传的文件列表
     * @param uploadDir 存储文件的目录
     * @param generateRandomName 是否生成随机唯一的文件名
     * @return 上传后文件相对于 {@code uploadDir} 的相对路径列表
     */
    public static CompletableFuture<List<String>> uploadFilesAsync(List<MultipartFile> files, String uploadDir, boolean generateRandomName) {
        // 将所有文件上传任务并行执行
        List<CompletableFuture<String>> uploadTasks = files.stream()
                .map(file -> uploadFileAsync(file, generateRandomName, uploadDir))
                .toList();

        // 等待所有任务完成并返回结果
        return CompletableFuture.allOf(uploadTasks.toArray(new CompletableFuture[0]))
                .thenApply(v -> uploadTasks.stream()
                        .map(CompletableFuture::join)
                        .collect(Collectors.toList()));
    }

    // 生成随机文件名
    public static String generateRandomFileName(MultipartFile file) {
        return UUID.randomUUID() + getFileExtension(file);
    }

    // 获取文件拓展名
    public static String getFileExtension(MultipartFile file) {
        String originalFilename = Objects.requireNonNull(file.getOriginalFilename());
        return originalFilename.substring(originalFilename.lastIndexOf("."));
    }

    /**
     * 将 MultipartFile 列表转换为 InputStream 列表
     * @param files MultipartFile 列表
     * @return InputStream 列表
     * @throws IOException 获取 InputStream 失败
     */
    public static InputStream[] convertToInputStreamArray(MultipartFile[] files) throws IOException {
        return Arrays.stream(files)
                .map(file -> {
                    try {
                        return file.getInputStream();
                    } catch (IOException e) {
                        throw new RuntimeException("Failed to get input stream", e);
                    }
                })
                .toArray(InputStream[]::new);
    }

    /**
     * 将 InputStream[] 转换为 MultipartFile[]
     *
     * @param inputStreams 输入流数组
     * @param fileMetaData 文件元数据数组
     * @return MultipartFile[]
     * @throws IOException 如果读取输入流失败
     */
    public static MultipartFile[] convertToMultipartFileArray(InputStream[] inputStreams, FileMetaData[] fileMetaData) throws IOException {
        if (inputStreams == null || fileMetaData == null || inputStreams.length != fileMetaData.length) {
            throw new IllegalArgumentException("输入流数组和文件元数据数组不能为空，且长度必须一致");
        }

        MultipartFile[] multipartFiles = new MultipartFile[inputStreams.length];
        for (int i = 0; i < inputStreams.length; i++) {
            InputStream inputStream = inputStreams[i];
            FileMetaData metaData = fileMetaData[i];

            // 将 InputStream 转换为字节数组
            byte[] content = inputStream.readAllBytes();

            // 创建 MockMultipartFile
            multipartFiles[i] = new CustomMultipartFile(
                    metaData.getName(),            // 表单字段名
                    metaData.getOriginalName(),    // 原始文件名
                    metaData.getContentType(),     // 文件类型
                    content                        // 文件内容
            );
        }
        return multipartFiles;
    }
}